#include "parser.h"
#include "str.h"
#include "hashmap.h"
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <strings.h>

struct type_t
{
    const char *name;
    int type;
};


static struct type_t g_dec[] = 
{
    {"optional", DECORATE_OPTIONAL},
    {"repeated", DECORATE_REPEATED},
    {"required", DECORATE_REQUIRED}
};

static struct type_t g_wire[] = 
{
    {"int32", PBC_INT32},
    {"int64", PBC_INT64},
    {"sint32", PBC_SINT32},
    {"sint64", PBC_SINT64},
    {"string", PBC_STRING},
    {"float", PBC_FLOAT},
    {"double", PBC_DOUBLE},
    {"message", PBC_MESSAGE}
};


static int message_parser(struct pbc *p, char *buf);


const char *wire_to_string(int wire)
{
    int i, sz;
    sz = sizeof(g_wire)/sizeof(g_wire[0]);

    for (i = 0; i < sz; ++i)
    {
        if (g_wire[i].type == wire)
        {
            return g_wire[i].name;
        }
    }
    return NULL;
}

struct pbc *pbc_create()
{
    struct pbc *p = malloc(sizeof(*p));
    if (p == NULL)
        return NULL;

    p->idx = 0;
    p->cap = INIT_SIZE;
    int size = p->cap * sizeof(struct pbc_userdef);
    p->userdef = calloc(1, size);
    p->msgs = hashmap_new();
    return p;
}

static void message_free(void *arg)
{
    struct pbc_env *msg = arg;
    struct pbc_field *ptr, *f = msg->header;

    while(f)
    {
        ptr = f;
        f = f->next;
        free(ptr);
    }

    free(msg);
}

void pbc_free(struct pbc *p)
{
    hashmap_dump(p->msgs, message_free);
    hashmap_release(p->msgs);

    free(p->userdef);
    free(p);
}

struct pbc_env *pbc_messae_create()
{
    struct pbc_env *msg = malloc(sizeof(*msg));
    msg->header = NULL;
    return msg;
}

static void check_userdef_alloc(struct pbc *p)
{
    if (p->idx > p->cap * 3/4)
    {
        int i, sz = p->idx << 1;
        struct pbc_userdef *uf = malloc(sz * sizeof(*uf));

        for (i = 0; i < p->idx; ++i)
        {
            uf[i] = p->userdef[i];
        }

        free(p->userdef);
        p->userdef = uf;
        p->cap = sz;
    }
}

static int check_keyword(struct pbc *p, struct pbc_field *f, char *buf)
{
    char *ptr = buf;
    char *p_msg = NULL;

    ptr += skip_space(ptr);

    p_msg = ptr;

    char name[128] = {0};
    ptr += get_word(ptr, name);

    // 检查修饰符
    int i, sz = sizeof(g_dec) / sizeof(g_dec[0]);
    for (i = 0; i < sz; ++i)
    {
        if (strcmp(name, g_dec[i].name) == 0)
        {
            f->dec_type = g_dec[i].type;
            break;
        }
    }

    if (f->dec_type)
    {
        ptr += skip_space(ptr);
        p_msg = ptr;
        memset(name, 0, sizeof(name));
        ptr += get_word(ptr, name);
    }

    // 检查字段类型
    sz = sizeof(g_wire) / sizeof(g_wire[0]);
    for (i = 0; i < sz; ++i)
    {
        if (strcmp(name, g_wire[i].name) == 0)
        {
            f->wire_type = g_wire[i].type;
            break;
        }
    }

    if (f->wire_type == PBC_MESSAGE)
    {
        p_msg += message_parser(p, p_msg);
        ptr = p_msg;
    }
    else if (!f->wire_type)
    {
        f->wire_type = PBC_MESSAGE;
        int idx = p->idx++;

        check_userdef_alloc(p);
        get_word(name, p->userdef[idx].name);
        p->userdef[idx].f = f;
    }

    return ptr - buf;
}


static int get_key_value(struct pbc_field *f, char *buf)
{
    char *ptr = buf;

    ptr += skip_space(ptr);
    ptr += get_word(ptr, f->name);

    while(*ptr++ != '=');

    ptr += skip_space(ptr);

    f->field_number = str_to_int(ptr);

    while(*ptr++ != ';');

    return ptr - buf;
}

static int field_parser(struct pbc *p, struct pbc_env *env, char *buf)
{
    char *ptr = buf;
    assert (*ptr == '{');
    ptr++;

    while(*ptr != '}')
    {
        struct pbc_field *f = calloc(1, sizeof(*f));
        ptr += check_keyword(p, f, ptr);
        if (f->dec_type == DECORATE_REPEATED)
        {
            env->array_sz ++;
        }

        if (*ptr == '}')
        {
            ptr++;
            ptr += skip_space(ptr);
            if (*ptr == '\0')
            {
                break;
            }
            else
            {
                free(f);
                continue;
            }
        }
        ptr += get_key_value(f, ptr);
        ptr += skip_space(ptr);
        
        if (env->header != NULL)
            f->next = env->header;
        
        env->field_sz++;
        env->header = f;
    }

    return ptr - buf;
}


static struct pbc_env *env_create()
{
    struct pbc_env *env = malloc(sizeof(*env));
    bzero(env, sizeof(*env));
    return env;
}

static int message_parser(struct pbc *p, char *buf)
{
    char *ptr = buf;

    int n = simple_cmp(ptr, "message");
    if (n > 0)
    {
        struct pbc_env *env = env_create();

        ptr += n;
        //至少要有一个空格
        assert(isspace(*ptr) > 0);

        //去除空格
        ptr += skip_space(ptr);
        ptr += get_word(ptr, env->name);
        ptr += skip_space(ptr);
        ptr += field_parser(p, env, ptr);

        // 存放一条消息
        hashmap_push(p->msgs, env->name, env);
    }

    return ptr - buf;
}

static void userdef_lookup(struct pbc *p)
{
    int i;
    for (i = 0; i < p->idx; ++i)
    {    
        struct pbc_field *f = p->userdef[i].f;
        const char *name = p->userdef[i].name;

        struct pbc_env *env = hashmap_get(p->msgs, name);
        if (env == NULL)
        {
            fprintf(stderr, "error: user define type(%s) is not exit\n", 
                    name);
            exit(1);
        }

        f->m = env;
    }
}

void dump(void *value)
{
    struct pbc_env *env = value;

    printf("%10s (field sz:%d, array sz: %d)--------\n", env->name, env->field_sz, env->array_sz);
    struct pbc_field *f = env->header;
    while(f)
    {
        printf("\t%10s: %10d\t", f->name, f->wire_type);
        if (f->m)
        {
            printf("-> %s\n", f->m->name);
        }
        else
        {
            printf("\n");
        }
        f = f->next;
    }
}

struct pbc *pbc_parser(char *buf)
{
    struct pbc *p = pbc_create();

    char *ptr = buf;
    while (*ptr != '\0')
    {
        ptr += message_parser(p, ptr);
        ptr++;
    }

    userdef_lookup(p);
    
    // hashmap_dump(p->msgs, dump);

    return p; 
}

struct pbc_env *pbc_env_query(struct pbc *p, const char *msgname)
{
    return hashmap_get(p->msgs, msgname);
}

struct pbc_field *pbc_field_query(struct pbc_env *env, const char *fname)
{
    struct pbc_field * f = env->header;
    while(f)
    {
        if (strcmp(f->name, fname) == 0)
            break;

        f = f->next;
    }

    return f;
}

struct pbc_field *pbc_field_query_fn(struct pbc_env *env, int fnumber)
{
    struct pbc_field * f = env->header;
    while(f)
    {
        if (f->field_number == fnumber)
            break;

        f = f->next;
    }

    return f;
}

